﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using ControleRepresentante.Logic;
using ControleRepresentante.Logic.Models;
using ControleRepresentante.Logic.Repositories;
using ControleRepresentante.Logic.Repositories.SearchExtensions;
using ControleRepresentante.Logic.Statistics;
using Telerik.Web.Mvc;
using WebSite.Controllers.ControllerExtensions;
using WebSite.ViewModels;
using WebSite.ViewModels.DetailsViewModels;
using WebSite.ViewModels.EditViewModels;
using WebSite.ViewModels.IndexViewModels;
using Attribute = ControleRepresentante.Logic.Models.Attribute;

namespace WebSite.Controllers
{
    public class ClienteController : UsuariosHolderControllerBase<IClienteRepository, Cliente, StatisticsBase>
    {
        public ClienteController(IRepositoryProvider repositoryProvider, ISessionConfiguration sessionConfiguration, IControllerExtensionProvider controllerExtensionProvider)
            : base(repositoryProvider, sessionConfiguration, controllerExtensionProvider)
        {
        }

        protected override bool CanDelete(Cliente model, out string message)
        {
            if (model.Pedidos.Any())
            {
                message = "Cliente nao pode ser excluido porque possui pedidos associados a ele.";
                return false;
            }

            if (model.Users.Any())
            {
                message = "Cliente não pode ser excluido porque possui usuários com filtros.";
                return false;
            }

            if (model.RelatorioVisitas.Any())
            {
                message = "Cliente não pode ser excluido porque possui relatorios de visitas associados a ele.";
                return false;
            }
            
            return base.CanDelete(model, out message);
        }

        protected override AttributeType AttributeType { get { return AttributeType.Cliente; } }

        protected override string CreateForActionName
        {
            get { return "CreateForCliente"; }
        }

        protected override UsuariosHolderDetailsViewModel DoGetUsuariosHolderDetailsViewModel(Cliente model)
        {
            return new ClienteDetailsViewModel(HasPermission(ControleRepresentante.Logic.Models.Area.Vendedores, Operacao.View));
        }

        protected override AttributeHolderEditViewModel GetEditViewModel()
        {
            IVendedorRepository vendedorRepository = RepositoryProvider.GetRepository<IVendedorRepository>();
            Dictionary<Guid, string> vendedores = vendedorRepository.GetModelNames();
            return new ClienteEditViewModel(vendedores);
        }

        protected override IAttributeHolderIndexViewModel GetAttributeHolderIndexViewModel(StatisticsBase statistics)
        {
            return new ClienteIndexViewModel(
                HasPermission(ControleRepresentante.Logic.Models.Area.Vendedores, Operacao.View),
                HasPermission(ControleRepresentante.Logic.Models.Area.Pedidos, Operacao.Create));
        }

        protected override void RegisterIndexColumns(IIndexColumnsProvider indexColumnsProvider)
        {
            base.RegisterIndexColumns(indexColumnsProvider);
            indexColumnsProvider.SetDefaultColumns(
                indexColumnsProvider.RegisterColumn("Razão Social", ClienteIndexColumns.RazaoSocial, true),
                indexColumnsProvider.RegisterColumn("Nome Fantasia", ClienteIndexColumns.NomeFantasia),
                indexColumnsProvider[DetailsHolderIndexColumns.Telefone],
                indexColumnsProvider[DetailsHolderIndexColumns.Fax],
                indexColumnsProvider[DetailsHolderIndexColumns.Email],
                indexColumnsProvider[DetailsHolderIndexColumns.Cidade],
                indexColumnsProvider[DetailsHolderIndexColumns.Bairro],
                indexColumnsProvider.RegisterColumn("Vendedor", ClienteIndexColumns.Vendedor));
            indexColumnsProvider.RegisterColumn("CNPJ", ClienteIndexColumns.CNPJ);
            indexColumnsProvider.RegisterColumn("Inscrição Estadual", ClienteIndexColumns.InscricaoEstadual);
            indexColumnsProvider.RegisterColumn("Cliente Desde", ClienteIndexColumns.ClienteDesde);
            indexColumnsProvider.RegisterColumn("Último Pedido", ClienteIndexColumns.UltimoPedido);
        }

        protected override Area? Area { get { return ControleRepresentante.Logic.Models.Area.Clientes; } }

        public ActionResult Search(string nome, string cidade, string bairro, string vendedorIds, DateTime? clienteDesdeDe, DateTime? clienteDesdeAte, DateTime? noPedidosDe, DateTime? noPedidosAte, FormCollection formCollection)
        {
            Dictionary<Attribute, string> attributeMap;
            StatisticsBase statistics;
            Dictionary<Guid, string> vendedores;
            IEnumerable<Cliente> models = RunSearch(vendedorIds, nome, cidade, bairro, clienteDesdeDe, clienteDesdeAte, noPedidosDe, noPedidosAte, true, formCollection, out attributeMap, out statistics, out vendedores);

            return Index(models, statistics, formCollection, x =>
            {
                ClienteIndexViewModel clienteIndexViewModel = (ClienteIndexViewModel)x;
                clienteIndexViewModel.VendedorBusca = vendedores;
                SetAttributeSearchValues(clienteIndexViewModel, attributeMap);
                SetDetailsSearchValues(clienteIndexViewModel, cidade, bairro);
                clienteIndexViewModel.NomeBuscaValue = nome;
                clienteIndexViewModel.VendedoresBuscaValue = vendedorIds;
                clienteIndexViewModel.ClienteDesdeDeBusca = clienteDesdeDe;
                clienteIndexViewModel.ClienteDesdeAteBusca = clienteDesdeAte;
                clienteIndexViewModel.NoPedidosDeBusca = noPedidosDe;
                clienteIndexViewModel.NoPedidosAteBusca = noPedidosAte;
            });
        }

        private IEnumerable<Cliente> RunSearch(string vendedorIds, string nome, string cidade, string bairro, DateTime? clienteDesdeDe, DateTime? clienteDesdeAte, DateTime? noPedidosDe, DateTime? noPedidosAte, bool usePaging, FormCollection formCollection, out Dictionary<Attribute, string> attributeMap, out StatisticsBase statistics, out Dictionary<Guid, string> vendedores)
        {
            EnsureHasPermission(Operacao.View);

            formCollection.Add(Request.QueryString);

            IAttributeRepository attributeRepository = RepositoryProvider.GetRepository<IAttributeRepository>();
            attributeMap = GetAttributeMap(attributeRepository, formCollection);
            vendedores = ParseIds<IVendedorRepository, Vendedor, StatisticsBase>(vendedorIds, x => x.Nome);
            ISearchExtension[] searchExtensions = GetSearchExtensions(formCollection);

            return Repository.GetModels(nome, cidade, bairro, vendedores.Keys, clienteDesdeDe, clienteDesdeAte, noPedidosDe, noPedidosAte, attributeMap, searchExtensions, out statistics, usePaging ? GetPage() : -1);
        }

        protected override bool CanPrint { get { return true; } }

        public ActionResult PrintList(string nome, string cidade, string bairro, string vendedorIds, DateTime? clienteDesdeDe, DateTime? clienteDesdeAte, DateTime? noPedidosDe, DateTime? noPedidosAte, FormCollection formCollection)
        {
            Dictionary<Attribute, string> attributeMap;
            StatisticsBase statistics;
            Dictionary<Guid, string> vendedores;
            IEnumerable<Cliente> models = RunSearch(vendedorIds, nome, cidade, bairro, clienteDesdeDe, clienteDesdeAte, noPedidosDe, noPedidosAte, false, formCollection, out attributeMap, out statistics, out vendedores);
            return PrintList(models, statistics, formCollection);
        }

        [HttpPost]
        public ActionResult Create(string razaoSocial, string nomeFantasia, Guid? vendedorId, string cnpj, string inscricaoEstadual, DateTime? clienteDesde, FormCollection formCollection = null)
        {
            EnsureHasPermission(Operacao.Create);
            if (TryUpdateCliente(razaoSocial, nomeFantasia, vendedorId, cnpj, inscricaoEstadual, clienteDesde, formCollection))
            {
                try
                {
                    Repository.AddModel(Model);
                    Cliente cliente = Model;
                    Model = null;
                    return RedirectToAction("Details", cliente.Id);
                }
                catch (RepositoryException<ClienteError> e)
                {
                    switch (e.Error)
                    {
                        case ClienteError.DuplicatedRazaoSocial:
                            ModelState.AddModelError("RazaoSocial", "Razão Social duplicada");
                            break;
                        default:
                            throw;
                    }
                }
            }
            return Create(false);
        }

        [HttpPost]
        public ActionResult Edit(string razaoSocial, string nomeFantasia, Guid? vendedorId, string cnpj, string inscricaoEstadual, DateTime?  clienteDesde, FormCollection formCollection = null)
        {
            EnsureHasPermission(Operacao.Update);
            if (TryUpdateCliente(razaoSocial, nomeFantasia, vendedorId, cnpj, inscricaoEstadual, clienteDesde, formCollection))
            {
                try
                {
                    Repository.UpdateModel(Model);
                    Guid clienteId = Model.Id;
                    Model = null;
                    return RedirectToAction("Details", clienteId);
                }
                catch (RepositoryException<ClienteError> e)
                {
                    switch (e.Error)
                    {
                        case ClienteError.DuplicatedRazaoSocial:
                            ModelState.AddModelError("RazaoSocial", "Razão Social duplicada");
                            break;
                        default:
                            throw;
                    }
                }
            }
            return Edit();
        }
        
        private bool TryUpdateCliente(string razaoSocial, string nomeFantasia, Guid? vendedorId, string cnpj, string inscricaoEstadual, DateTime? clienteDesde, FormCollection formCollection)
        {
            Model.RazaoSocial = razaoSocial;
            Model.NomeFantasia = nomeFantasia;
            Model.ClienteDesde = clienteDesde;
            if (vendedorId.HasValue)
            {
                IVendedorRepository vendedorRepository = RepositoryProvider.GetRepository<IVendedorRepository>();
                Model.Vendedor = vendedorRepository.GetModelById(vendedorId.Value);
            }
            else
            {
                Model.Vendedor = null;
            }
            Model.CNPJ = cnpj;
            Model.InscricaoEstadual = inscricaoEstadual;

            UpdateAttributesToModel(formCollection);

            ModelState.Clear();
            if (String.IsNullOrEmpty(Model.RazaoSocial) ||
                Model.RazaoSocial.Trim() == String.Empty)
            {
                ModelState.AddModelError("RazaoSocial", "*");
            }
            ExtensionsUpdateModel(formCollection);
            return ModelState.IsValid;
        }

        public ViewResult Print(Guid id)
        {
            return View("PrintFicha", (ClienteDetailsViewModel) GetDetailsViewModel(id));
        }

        #region Endereco

        [GridAction]
        public ActionResult ClienteEnderecoSelect()
        {
            return GetView();
        }

        [GridAction]
        [HttpPost]
        public ActionResult ClienteEnderecoInsert(string tipo, string rua, string numero, string complemento, string bairro, string cep, string cidade, string estado, string observacao, bool principal, bool enderecoCobranca, bool enderecoEntrega)
        {
            Endereco endereco = new Endereco();
            if (TryUpdateEndereco(endereco, tipo, rua, numero, complemento, bairro, cep, cidade, estado, observacao, principal, enderecoCobranca, enderecoEntrega))
            {
                Model.AddEndereco(endereco);
            }
            return GetView();
        }

        [GridAction]
        [HttpPost]
        public ActionResult ClienteEnderecoDelete(Guid enderecoId)
        {
            Endereco endereco = Model.Enderecos.Single(x => x.Id == enderecoId);
            Model.RemoveEndereco(endereco);
            if (endereco.Principal)
            {
                Endereco principalForTipo = Model.Enderecos.FirstOrDefault(x => x.Tipo == endereco.Tipo);
                if (principalForTipo != null)
                {
                    principalForTipo.Principal = true;
                }
            }
            if (Model.EnderecoCobranca.Id == enderecoId)
            {
                IEnumerable<Endereco> enderecosForTipo = Model.Enderecos.Where(x => x.Tipo == endereco.Tipo);
                Model.EnderecoCobranca = enderecosForTipo.Any()
                    ? enderecosForTipo.Single(x => x.Principal)
                    : Model.Enderecos.FirstOrDefault(x => x.Principal);
            }
            if (Model.EnderecoEntrega.Id == enderecoId)
            {
                IEnumerable<Endereco> enderecosForTipo = Model.Enderecos.Where(x => x.Tipo == endereco.Tipo);
                Model.EnderecoEntrega = enderecosForTipo.Any()
                    ? enderecosForTipo.Single(x => x.Principal)
                    : Model.Enderecos.FirstOrDefault(x => x.Principal);
            }
            return GetView();
        }

        [GridAction]
        [HttpPost]
        public ActionResult ClienteEnderecoUpdate(Guid enderecoId, string tipo, string rua, string numero, string complemento, string bairro, string cep, string cidade, string estado, string observacao, bool principal, bool enderecoCobranca, bool enderecoEntrega)
        {
            Endereco endereco = Model.Enderecos.Single(x => x.Id == enderecoId);
            TryUpdateEndereco(endereco, tipo, rua, numero, complemento, bairro, cep, cidade, estado, observacao, principal, enderecoCobranca, enderecoEntrega);
            return GetView();
        }

        private ActionResult GetView()
        {
            return View(new GridModel(Model.Enderecos.Select(endereco => new ClienteEnderecoViewModel(endereco, Model))));
        }

        private bool TryUpdateEndereco(Endereco endereco, string tipo, string rua, string numero, string complemento, string bairro, string cep, string cidade, string estado, string observacao, bool principal, bool enderecoCobranca, bool enderecoEntrega)
        {
            endereco.Tipo = (EnderecoType)Enum.Parse(typeof(EnderecoType), tipo);
            endereco.Rua = rua;
            endereco.Numero = numero;
            endereco.Complemento = complemento;
            endereco.Bairro = bairro;
            endereco.CEP = cep;
            endereco.Cidade = cidade;
            endereco.Estado = estado;
            endereco.Observacao = observacao;
            if (endereco.Rua != null && !String.IsNullOrEmpty(endereco.Rua) &&
                endereco.Numero != null && !String.IsNullOrEmpty(endereco.Numero) &&
                endereco.Cidade != null && !String.IsNullOrEmpty(endereco.Cidade) &&
                endereco.Estado != null && !String.IsNullOrEmpty(endereco.Estado))
            {
                Endereco principalEnderecoForTipo = Model.Enderecos.FirstOrDefault(x => x.Tipo == endereco.Tipo && x.Principal && x.Id != endereco.Id);

                if (principalEnderecoForTipo == null)
                {
                    endereco.Principal = true;
                }
                else if (principal)
                {
                    endereco.Principal = true;
                    principalEnderecoForTipo.Principal = false;
                }
                else
                {
                    endereco.Principal = false;
                }

                if (enderecoCobranca || Model.EnderecoCobranca == null)
                {
                    Model.EnderecoCobranca = endereco;
                }
                if (enderecoEntrega || Model.EnderecoEntrega == null)
                {
                    Model.EnderecoEntrega = endereco;
                }
                return true;
            }
            return false;
        }

        #endregion
    }
}
